#ifndef __CMD_OPTIONS_H__
#define __CMD_OPTIONS_H__
#include "common.h"
#include <map>
#include <vector>
#include <iostream>
#include <string>
#include <getopt.h>

BEG_DEF_NAMESPACE_SMTPUTILS

typedef enum{
	BOOL,
	INT,
	FLOAT,
	STRING,
} OPTS_TYPE ;

typedef struct _ANY_TYPE{
	_ANY_TYPE(){
		content_bool = false;
		content_int = 0;
		content_float = 0.0f;
		content_string ="";
	}
	bool		content_bool;
	int		content_int;
	float		content_float;
	std::string	content_string;
} ANY_TYPE;

typedef struct {
	bool has_default;
	ANY_TYPE content;
} RESULT_TYPE;


/*
 * type		: Argument type
 * short 	: Short argument option
 * long 	: Long argument option
 * description 	: Despcription of this argument
 * has_arg 	: Is there content following the option
 *
 * result_value.has_default : If true, this option is selected default and the result_value.content is the default value , 
 * 				or not the default option
 * result_value.content	: It contain the content of this option if it has content when this option appeared in the argument list , 
 * 				or it contains the default value
 */
typedef struct opts{
	OPTS_TYPE opt_type;
	char short_opt_char;
	std::string long_opt_string;
	std::string instruction;
	bool has_arg;	
	RESULT_TYPE result_value ;
} OPT_STRUCT;

class CmdOptions{
public:
	static CmdOptions * GetInstance();

	void InsertOptBOOL(uint32_t id, 
			char shortChar, 
			const std::string& longStr, 
			const std::string& description, 
			bool hasArg, 
			bool hasDefault, 
			bool defValue);

	void InsertOptINT(uint32_t id,
			char shortChar, 
			const std::string& longStr, 
			const std::string& description, 
			bool hasArg, 
			bool hasDefault, 
			int defValue);

	void InsertOptFLOAT(uint32_t id,
			char shortChar, 
			const std::string& longStr, 
			const std::string& description, 
			bool hasArg, 
			bool hasDefault, 
			float defValue);

	void InsertOptSTRING(uint32_t id,
			char shortChar, 
			const std::string& longStr, 
			const std::string& description, 
			bool hasArg, 
			bool hasDefault, 
			const std::string& defValue);

	int32_t HandleOpts( const std::string& versionInfo,int argc , char ** argv );

	int32_t BuildDescriptionString();
	int32_t BuildDescriptionOption();

	void PrintArgs( OPT_STRUCT opt );

	bool HasEnable( uint32_t id );
	bool GetBool( uint32_t id, bool& retBool );
	bool GetInt( uint32_t id, int& retInt);
	bool GetFloat( uint32_t id, float& retFloat );
	bool GetString( uint32_t id, std::string& retString );
	
	void Usage() const;
private:
	void InsertOptBase(OPT_STRUCT & opt,
			char shortChar, 
			const std::string& longStr, 
			const std::string& description, 
			bool hasArg, 
			bool hasDefault); 
	void SetRetValue( OPT_STRUCT& rOpts, char* OptArg );
	CmdOptions();
private:
	typedef std::map< uint32_t , OPT_STRUCT > OptsContainer;
	typedef std::vector< struct option > OptionsVec;	

	std::string mProgramName;
	std::string mVersionInfo;	
	std::string mDescriptionString;
	OptionsVec mOptionsVec;
	OptsContainer mOptsBlob;
};

#define DEF_OPTION( ID, TYPE, SHORT, LONG, HASARG, HASDEF, DEFVALUE, DESC )					\
do{														\
	CmdOptions::GetInstance()->InsertOpt##TYPE( (ID),(SHORT),(LONG),(DESC),(HASARG),(HASDEF),(DEFVALUE) );	\
}while(0)

END_DEF_NAMESPACE_SMTPUTILS
#endif

